﻿#include "stdafx.h"
#include "sbhs.h"
#include "sbctn.h"
#include "extend/sberror.h"
#include "kh_compiler.h"
#include "kh_code.h"
#include <float.h>

#ifndef k_eq
#define k_eq(a,b)		(((a)+0.0001f>(b)) && ((a)-0.0001f<(b)))
#endif

//////////////////////////////////////////////////////////////////////////
// 분석

//
K_SLIST_DECL(khTkList, khToken);
K_SLIST_DECL(khStrList, kchar*);
K_SLIST_DECL(khWcsList, kwchar*);

static void kh_token_init(khToken* tk, kint ln, kint col, enum khTokenType type, enum khTokenRole role);
static void _kh_token_set_attach(khToken* tk, khToken* node);
static void _kh_token_write_file(khToken* tk, kFile* file);

//
static struct khImplParse
{
	YY_BUFFER_STATE		sbuffer;

	khToken*			top;

	khTkList*			tks;
	khStrList*			strs;
	khWcsList*			wcss;

	kint				ident;
	kint				tmpval;
	ksize_t				memuse;
} kh_impl_parse =
{
	NULL,

	NULL,

	NULL,
	NULL,
	NULL,

	0,
	0,
	0,
};

// 들어감
kcham kh_parse_enter(const char* filename, const char* script, kcham isutf8)
{
	kint err;

	khs_filename = filename;
	khs_lineno = 1;
	khs_column = 1;
	khs_utf8 = isutf8;

#ifndef SB_NO_MEM
	kh_impl_parse.memuse = k_heapsize();
#endif

	kh_impl_parse.sbuffer = khs__scan_string(script);

	if (kh_impl_parse.sbuffer)
	{
		err = khs_parse();
		khs__delete_buffer(kh_impl_parse.sbuffer);
	}
	else
	{
		err = 1;
		kh_parse_error(KERR_PARSE, "scan error");
	}

#ifndef SB_NO_MEM
	kh_impl_parse.memuse = k_heapsize() - kh_impl_parse.memuse;
#endif

	if (err != 0)
	{
		kh_parse_leave();
		return FALSE;
	}

	return TRUE;
}

// 나감
void kh_parse_leave(void)
{
	k_slist_loopeach(khWcsList, kh_impl_parse.wcss, k_free);
	k_slist_disp(khWcsList, kh_impl_parse.wcss);
	k_slist_loopeach(khStrList, kh_impl_parse.strs, k_free);
	k_slist_disp(khStrList, kh_impl_parse.strs);
	k_slist_disp(khTkList, kh_impl_parse.tks);

	kh_impl_parse.wcss = NULL;
	kh_impl_parse.strs = NULL;
	kh_impl_parse.tks = NULL;

	kh_impl_parse.top = NULL;
	kh_impl_parse.sbuffer = NULL;
}

//
void kh_parse_error(kint code, const char* txt)
{
	k_mesg_error(TRUE, khs_lineno, khs_column, khs_filename, code, "parse", txt);
}

//
ksize_t kh_parse_get_mem_use()
{
	return kh_impl_parse.memuse;
}

// 찍어보기
void kh_parse_write_file(kFile* file)
{
	kBstr260 bs;
#if 0
	char uds[64];
#endif

	k_return_if_fail(file);
	k_return_if_fail(kh_impl_parse.top);

#if 0
	if (khs_utf8)
	{
		kint sig = 0x00BFBBEF;
		k_file_write(file, &sig, 0, 3 * sizeof(hbbyte));
	}
#endif

	k_bstr_set(&bs, "\n");
	k_file_write(file, bs.data, 0, bs.len);

	kh_impl_parse.ident = -2;
	_kh_token_write_file(kh_impl_parse.top, file);

#if 0
	k_memuds(uds, 64 - 1, kh_impl_parse.memuse);
	k_bstr_format(&bs, 260, "\n* parser memory usage: %sbytes.\n", uds);
	k_file_write(file, bs.data, 0, bs.len);
#endif
}

//
void kh_parse_set_token(khToken* node)
{
	kh_impl_parse.top = node;
}

//
khToken* kh_parse_get_token()
{
	return kh_impl_parse.top;
}

//
khToken* kh_parse_alloc_token()
{
	k_slist_reserve_prepend(khTkList, kh_impl_parse.tks, &kh_impl_parse.tks);
	return &kh_impl_parse.tks->data;
}

//
khToken* kh_parse_new_token(enum khTokenType type, enum khTokenRole role)
{
	khToken* tk = kh_parse_alloc_token();
	kh_token_init(tk, khs_lineno, khs_column, type, role);
	return tk;
}

//
khToken* kh_parse_new_token_info(khToken* info, enum khTokenType type, enum khTokenRole role)
{
	if (!info)
		return kh_parse_new_token(type, role);
	else
	{
		khToken* tk = kh_parse_alloc_token();
		kh_token_init(tk, info->line, info->column, type, role);
		return tk;
	}
}

//
khToken* kh_parse_new_op(enum khTokenRole role, khToken* left, khToken* right)
{
	khToken* tk = kh_parse_new_token(KHTKT_OP, role);
	tk->flags = KHTKF_EXPR_PARENT;
	kh_token_set_child(tk, 0, left, FALSE);
	kh_token_set_child(tk, 1, right, FALSE);
	return tk;
}

//
khToken* kh_parse_copy_token(khToken* token)
{
	khToken* tk = kh_parse_alloc_token();
	memcpy(tk, token, sizeof(khToken));
	return tk;
}

//
khToken* kh_parse_link_token(khToken* left, khToken* right)
{
	if (!left)
		return right;
	else
	{
		_kh_token_set_attach(left, right);
		return left;
	}
}

//
char* kh_parse_alloc_str_init(const char* str)
{
	khStrList* node;
	k_slist_contains_func(khStrList, kh_impl_parse.strs, k_streqv, str, &node);

	if (node)
		return node->data;
	else
	{
		char* psz = k_strdup(str);
		k_slist_prepend(khStrList, kh_impl_parse.strs, psz, &kh_impl_parse.strs);
		return psz;
	}
}

//
char* kh_parse_alloc_str_size(ksize_t size)
{
	char* ret = k_new(size + 1, char);
	*ret = '\0';
	k_slist_prepend(khStrList, kh_impl_parse.strs, ret, &kh_impl_parse.strs);
	return ret;
}

//
char* kh_parse_alloc_str_conv(const char* str)
{
	return kh_parse_alloc_str_init(str);
}

//
kwchar* kh_parse_alloc_wcs_init(const char* str)
{
	ksize_t len;
	kwchar* pwz;
	khWcsList* node;

#if _SB_WINDOWS_
	len = k_conv_utf8_to_utf16(NULL, 0, str, 0);
	pwz = k_new(len + 1, kwchar);
	k_conv_utf8_to_utf16(pwz, len + 1, str, 0);
#else
	// 윈도우 이외에는 무조건 UCS4로 함
	len = k_conv_utf8_to_ucs4(NULL, 0, str, 0);
	pwz = k_new(len + 1, kwchar);
	k_conv_utf8_to_ucs4(pwz, len + 1, str, 0);
#endif

	k_slist_contains_func(khWcsList, kh_impl_parse.wcss, k_wcseqv, pwz, &node);

	if (node)
	{
		k_delete(pwz);
		return node->data;
	}
	else
	{
		k_slist_prepend(khWcsList, kh_impl_parse.wcss, pwz, &kh_impl_parse.wcss);
		return pwz;
	}
}

//
klong kh_parse_check_int(const char* str, kint base, kcham isint)
{
	kBstr260 bs;
	klong n = (base == 10) ? k_strtol(str) : (klong)k_strtol_base(str, base);

	if (isint)
	{
		if (n < K_MIN_INT)
		{
			k_bstr_format(&bs, 260, "underflow int value. (value: %lld, min: %lld)", n, (klong)K_MIN_INT);
			kh_parse_error(KERR_UNDERFFLOW, bs.data);
		}
		else if (n > K_MAX_INT)
		{
			k_bstr_format(&bs, 260, "overflow int value. (value: %lld, max: %lld)", n, (klong)K_MAX_INT);
			kh_parse_error(KERR_OVERFLOW, bs.data);
		}
	}

	return n;
}

//
double kh_parse_check_float(const char* str, kcham isfloat)
{
	kBstr260 bs;
	double d = k_atod(str);

	if (isfloat)
	{
#if 0

		if (d < (double)FLT_MIN)
		{
			k_bstr_format(&bs, 260, "underflow float value. (value: %f, min: %f)", d, FLT_MIN);
			kh_parse_error(KERR_UNDERFFLOW, bs.data);
		}
		else
#endif
		if (d > (double)FLT_MAX)
		{
			k_bstr_format(&bs, 260, "overflow float value. (value: %f, max: %f)", d, FLT_MAX);
			kh_parse_error(KERR_OVERFLOW, bs.data);
		}
	}

	return d;
}

//
kint kh_parse_check_char(const char* str, kcham isuni)
{
	kint v = 0, l = 0;
	ksize_t len;

	// 사실 유니코드라고 해도 여기서 나눠보는 글자는 127이하.
	// 그러므로 유니코드로 따로 할 필요는 없는데
	// 일단 #IF ~ #ENDIF 묶음

#if 1
	if (isuni)
	{
		kwchar* s, *c;

		len = k_conv_utf8_to_uni(NULL, 0, str, 0);
		c = s = (kwchar*)alloca((len + 1) * sizeof(kwchar));
		k_conv_utf8_to_uni(s, len + 1, str, 0);

		while (*c)
		{
			if (*c == L'\'')
			{
				c++;
				continue;
			}
			else if (*c == L'\\')
			{
				if (l)
					v <<= 8;

				switch (*(c + 1))
				{
					case L'a':
						v |= L'\a';
						break;

					case L'b':
						v |= L'\b';
						break;

					case L'f':
						v |= L'\f';
						break;

					case L'n':
						v |= L'\n';
						break;

					case L'r':
						v |= L'\r';
						break;

					case L't':
						v |= L'\t';
						break;

					case L'v':
						v |= L'\v';
						break;

					case L'"':
						v |= L'\"';
						break;

					case L'\'':
						v |= L'\'';
						break;

					case L'\\':
						v |= L'\\';
						break;

					default:
						v |= *(c + 1);
						break;
				}

				l++;
				c += 2;
				continue;
			}

			if (l)
				v <<= 8;

			v |= *(c++);
			l++;
		}

		return v;
	}
	else
#endif
	{
		char* s, *c;

		len = k_strlen(str);
		c = s = (char*)alloca(len + 1);
		k_strcpy(s, str);

		while (*c)
		{
			if (*c == '\'')
			{
				c++;
				continue;
			}
			else if (*c == '\\')
			{
				if (l)
					v <<= 8;

				switch (*(c + 1))
				{
					case 'a':
						v |= '\a';
						break;

					case 'b':
						v |= '\b';
						break;

					case 'f':
						v |= '\f';
						break;

					case 'n':
						v |= '\n';
						break;

					case 'r':
						v |= '\r';
						break;

					case 't':
						v |= '\t';
						break;

					case 'v':
						v |= '\v';
						break;

					case '"':
						v |= '\"';
						break;

					case '\'':
						v |= '\'';
						break;

					case '\\':
						v |= '\\';
						break;

					default:
						v |= *(c + 1);
						break;
				}

				l++;
				c += 2;
				continue;
			}

			if (l)
				v <<= 8;

			v |= *(c++);
			l++;
		}

		return v;
	}
}

//
char* kh_parse_check_str(const char* text)
{
	char* psz;
	ksize_t len = k_strlen(text);

	switch (*text)
	{
		case '@':
			psz = (char*)alloca(len);
			k_strcpy(psz, text + 1);
			k_strsquote(psz, '"');
			break;

		case '#':
			psz = (char*)alloca(len);
			k_strcpy(psz, text + 1);
			k_strsquote(psz, '"');
			break;

		case '"':
			psz = (char*)alloca(len + 1);
			k_strcpy(psz, text);
			k_strdquote(psz, '"');
			break;

		case '`':
			psz = (char*)alloca(len + 1);
			k_strcpy(psz, text);
			k_strdquote(psz, '`');
			break;

		default:
			kh_parse_error(KERR_SYNTAX, "invalid string value.");
			return NULL;
	}

	return kh_parse_alloc_str_conv(psz);
}

//
kwchar* kh_parse_check_wcs(const char* text)
{
	char* psz;
	ksize_t len = k_strlen(text);

	if (*text != 'L')
	{
		kh_parse_error(KERR_SYNTAX, "invalid wide string value.");
		return NULL;
	}

	psz = (char*)alloca(len + 1);
	k_strcpy(psz, text + 1);
	k_strdquote(psz, '"');

	return kh_parse_alloc_wcs_init(psz);
}

kint kh_parse_printf(FILE* fp, const char* fmt, ...)
{
	va_list va;
	char* buf;
	kint len;
	va_start(va, fmt);
	len = k_vasprintf(&buf, fmt, va);
	va_end(va);
	k_mesg(buf);
	k_delete(buf);
	return len;
}


//////////////////////////////////////////////////////////////////////////
// 토큰

//
static void kh_token_init(khToken* tk, kint ln, kint col, enum khTokenType type, enum khTokenRole role)
{
	tk->line = ln;
	tk->column = col;
	tk->type = type;
	tk->role = role;
}

//
void kh_token_set_child(khToken* tk, kint index, khToken* node, kcham sib)
{
	tk->child[index] = node;

	if (!sib)
	{
		if (node)
			node->parent = tk;
	}
	else
	{
		while (node)
		{
			node->parent = tk;
			node = node->next;
		}
	}
}

//
void kh_token_set_mask(khToken* tk, kint add, kint sub)
{
	if (add)
		tk->flags = (enum khTokenFlag)(tk->flags | add);

	if (sub)
		tk->flags = (enum khTokenFlag)(tk->flags & sub);
}

//
static void _kh_token_set_attach(khToken* tk, khToken* node)
{
	khToken* tmp = tk;

	while (tmp->next != NULL)
		tmp = tmp->next;

	tmp->next = node;

	if (node)
		node->prev = tmp;
}

//
static void _kh_token_write_file(khToken* tk, kFile* file)
{
	khToken* p;
	char* psz;
	ksize_t size, i;

	kh_impl_parse.ident += 4;

	p = tk;

	while (p)
	{
		kBstr1k bs;

		k_bstr_format(&bs, 1024, "%-4d", p->line);
		k_bstr_append_rep(&bs, ' ', kh_impl_parse.ident);

		switch (p->type)
		{
			case KHTKT_IDENTIFIER:  // 식별자
				if (p->role == KHTKR_ID_UNKNOWN)
					k_bstr_append_format(&bs, 1024, "'%s'", p->value.str);
				else
					k_bstr_append_format(&bs, 1024, "%s.'%s'", kh_tos_token_role(p->role), p->value.str);

				break;

			case KHTKT_CONSTANT:    // 상수
				switch (p->role)
				{
					case KHTKR_CONST_NULL:
						k_bstr_append(&bs, "Null");
						break;

					case KHTKR_CONST_BOOL:
						k_bstr_append_format(&bs, 1024, "Bool -> %s", p->value.i64 ? "TRUE" : "FALSE");
						break;

					case KHTKR_CONST_INT:
						k_bstr_append_format(&bs, 1024, "Int -> %d (0x%X)", (kint)p->value.i64, (kint)p->value.i64);
						break;

					case KHTKR_CONST_LONG:
						k_bstr_append_format(&bs, 1024, "Long -> %lld (0x%llX)", p->value.i64, p->value.i64);
						break;

					case KHTKR_CONST_FLOAT:
						k_bstr_append_format(&bs, 1024, "Float -> %f", p->value.dbl);
						break;

					case KHTKR_CONST_DOUBLE:
						k_bstr_append_format(&bs, 1024, "Double -> %f", p->value.dbl);
						break;

					case KHTKR_CONST_STRING:
						psz = k_strdup(p->value.str);
						k_strrem(psz, "\n\r\t\v\a\b\f");
						k_bstr_append_format(&bs, 1024, "%s -> \"%s\"", (p->flags & KHTKF_SYMBOL) ? "Symbol" : "String", psz);
						k_delete(psz);
						break;

					case KHTKR_CONST_WIDESTRING:
						size = k_conv_uni_to_utf8(NULL, 0, p->value.wcs, 0);
						psz = k_new(size + 1, char);
						k_conv_uni_to_utf8(psz, size, p->value.wcs, 0);
						k_strrem(psz, "\n\r\t\v\a\b\f");
						k_bstr_append_format(&bs, 1024, "Wide String -> \"%s\"", psz);
						k_delete(psz);
						break;

					default:
						k_bstr_append_format(&bs, 1024, "unknown constant (role=%d)", p->role);
						break;
				}

				break;

			case KHTKT_ARG:         // 인수
			case KHTKT_DECL:        // 선언
			case KHTKT_STMT:        // 형식
			case KHTKT_EXPR:        // 식
			case KHTKT_OP:          // 연산
			case KHTKT_KEYWORD:     // 키워드
				k_bstr_append(&bs, kh_tos_token_role(p->role));
				break;

			default:
				k_bstr_append_format(&bs, 1024, "unknown type (type=%d, role=%d", p->type, p->role);
				break;
		}

		k_bstr_append_char(&bs, '\n');
		k_file_write(file, bs.data, 0, bs.len);

		for (i = 0; i < 4; i++)
		{
			if (p->child[i])
				_kh_token_write_file(p->child[i], file);
		}

		p = p->next;
	}

	kh_impl_parse.ident -= 4;
}

//
static klong _kh_fold_long_unary(klong n, kint op)
{
	switch (op)
	{
		case KHTKR_OP_UNARY_PLUS:
			return n;

		case KHTKR_OP_UNARY_MINUS:
			return -n;

		case KHTKR_OP_UNARY_SIG:
			return !n;

		case KHTKR_OP_UNARY_NOT:
			return ~n;

		case KHTKR_OP_UNARY_INC:
			return n + 1;

		case KHTKR_OP_UNARY_DEC:
			return n - 1;
	}

	return 0;
}

//
static klong _kh_fold_long_binary(klong l, klong r, kint op)
{
	switch (op)
	{
		case KHTKR_OP_MUL:
			return l * r;

		case KHTKR_OP_DIV:
			return (r == 0) ? 0 : l / r;

		case KHTKR_OP_MOD:
			return l % r;

		case KHTKR_OP_ADD:
			return l + r;

		case KHTKR_OP_SUB:
			return l - r;

		case KHTKR_OP_SHL:
			return l << r;

		case KHTKR_OP_SHR:
			return l >> r;

		case KHTKR_OP_BINARY_AND:
			return l & r;

		case KHTKR_OP_BINARY_XOR:
			return l ^ r;

		case KHTKR_OP_BINARY_OR:
			return l | r;
	}

	return 0;
}

//
static kcham _kh_fold_long_cmp(klong l, klong r, kint op)
{
	switch (op)
	{
		case KHTKR_OP_LT:
			return l < r;

		case KHTKR_OP_LE:
			return l <= r;

		case KHTKR_OP_EQ:
			return l == r;

		case KHTKR_OP_NEQ:
			return l != r;
	}

	return FALSE;
}

//
static double _kh_fold_double_unary(double n, kint op)
{
	switch (op)
	{
		case KHTKR_OP_UNARY_PLUS:
			return n;

		case KHTKR_OP_UNARY_MINUS:
			return -n;

		case KHTKR_OP_UNARY_INC:
			return n + 1.0;

		case KHTKR_OP_UNARY_DEC:
			return n - 1.0;
	}

	return 0.0;
}

//
static double _kh_fold_double_binary(double l, double r, kint op)
{
	switch (op)
	{
		case KHTKR_OP_MUL:
			return l * r;

		case KHTKR_OP_DIV:
			return k_eq(r, 0.0) ? 0.0 : l / r;

		case KHTKR_OP_MOD:
			return fmod(l, r);

		case KHTKR_OP_ADD:
			return l + r;

		case KHTKR_OP_SUB:
			return l - r;
	}

	return 0.0;
}

//
static kcham _kh_fold_double_cmp(double l, double r, kint op)
{
	switch (op)
	{
		case KHTKR_OP_LT:
			return l < r;

		case KHTKR_OP_LE:
			return l <= r;

		case KHTKR_OP_EQ:
			return l == r;

		case KHTKR_OP_NEQ:
			return l != r;
	}

	return FALSE;
}

//
static char* _kh_fold_long_add_string(klong l, char* r)
{
	ksize_t ll, rl;
	char lt[64];
	char* s;

	ll = k_snprintf(lt, 64, "%lld", l);
	rl = k_strlen(r);

	s = (char*)alloca(ll + rl + 1);
	k_strcpy(s, lt);
	k_strcpy(s + ll, r);

	return kh_parse_alloc_str_init(s);
}

//
static char* _kh_fold_string_add_long(char* l, klong r)
{
	ksize_t ll, rl;
	char rt[64];
	char* s;

	ll = k_strlen(l);
	rl = k_snprintf(rt, 64, "%lld", r);

	s = (char*)alloca(ll + rl + 1);
	k_strcpy(s, l);
	k_strcpy(s + ll, rt);

	return kh_parse_alloc_str_init(s);
}

//
static char* _kh_fold_double_add_string(double l, char* r)
{
	ksize_t ll, rl;
	char lt[64];
	char* s;

	ll = k_snprintf(lt, 64, "%f", l);
	rl = k_strlen(r);

	s = (char*)alloca(ll + rl + 1);
	k_strcpy(s, lt);
	k_strcpy(s + ll, r);

	return kh_parse_alloc_str_init(s);
}

//
static char* _kh_fold_string_add_double(char* l, double r)
{
	ksize_t ll, rl;
	char rt[64];
	char* s;

	ll = k_strlen(l);
	rl = k_snprintf(rt, 64, "%f", r);

	s = (char*)alloca(ll + rl + 1);
	k_strcpy(s, l);
	k_strcpy(s + ll, rt);

	return kh_parse_alloc_str_init(s);
}

//
static char* _kh_fold_string_add_string(char* l, char* r)
{
	ksize_t ll, rl;
	char* s;

	ll = k_strlen(l);
	rl = k_strlen(r);

	s = (char*)alloca(ll + rl + 1);
	k_strcpy(s, l);
	k_strcpy(s + ll, r);

	return kh_parse_alloc_str_init(s);
}

//
static kcham _kh_fold_long_cmp_string(klong l, char* r, kint op)
{
	ksize_t len = k_strlen(r);
	klong n;

	if (len < 2)
		n = k_atol(r);
	else
	{
		if (r[0] != '0')
			n = k_atol(r);
		else if (r[1] == 'x' || r[1] == 'X')
			n = (klong)k_atol_h(r + 2);
		else if (r[1] == 'b' || r[1] == 'B')
			n = (klong)k_atol_b(r + 2);
		else
			n = k_atol(r);
	}

	switch (op)
	{
		case KHTKR_OP_LT:
			return l < n;

		case KHTKR_OP_LE:
			return l <= n;

		case KHTKR_OP_EQ:
			return l == n;

		case KHTKR_OP_NEQ:
			return l != n;
	}

	return FALSE;
}

//
static kcham _kh_fold_string_cmp_long(char* l, klong r, kint op)
{
	ksize_t len = k_strlen(l);
	klong n;

	if (len < 2)
		n = k_atol(l);
	else
	{
		if (l[0] != '0')
			n = k_atol(l);
		else if (l[1] == 'x' || l[1] == 'X')
			n = (klong)k_atol_h(l + 2);
		else if (l[1] == 'b' || l[1] == 'B')
			n = (klong)k_atol_b(l + 2);
		else
			n = k_atol(l);
	}

	switch (op)
	{
		case KHTKR_OP_LT:
			return n < r;

		case KHTKR_OP_LE:
			return n <= r;

		case KHTKR_OP_EQ:
			return n == r;

		case KHTKR_OP_NEQ:
			return n != r;
	}

	return FALSE;
}

//
static kcham _kh_fold_double_cmp_string(double l, char* r, kint op)
{
	double d = k_atod(r);

	switch (op)
	{
		case KHTKR_OP_LT:
			return l < d;

		case KHTKR_OP_LE:
			return l <= d;

		case KHTKR_OP_EQ:
			return l == d;

		case KHTKR_OP_NEQ:
			return l != d;
	}

	return FALSE;
}

//
static kcham _kh_fold_string_cmp_double(char* l, double r, kint op)
{
	double d = k_atod(l);

	switch (op)
	{
		case KHTKR_OP_LT:
			return d < r;

		case KHTKR_OP_LE:
			return d <= r;

		case KHTKR_OP_EQ:
			return d == r;

		case KHTKR_OP_NEQ:
			return d != r;
	}

	return FALSE;
}

//
static kcham _kh_fold_string_cmp_string(char* l, char* r, kint op)
{
	int n = k_strcmp(l, r);

	switch (op)
	{
		case KHTKR_OP_LT:
			return n < 0;

		case KHTKR_OP_LE:
			return n <= 0;

		case KHTKR_OP_EQ:
			return n == 0;

		case KHTKR_OP_NEQ:
			return n != 0;
	}

	return FALSE;
}

//
kcham kh_token_fold(khToken* tk)
{
	kcham isunary, isfold, isint, isstr, iscmp;
	enum khTokenRole lr, rr;
	khToken* l, *r;
	char* psz;
	char sz[260];
	kint role;

	k_return_value_if_fail(tk->type == KHTKT_OP, FALSE);

	isunary = isfold = isint = isstr = iscmp = FALSE;

	role = tk->role;

	switch (role)
	{
		case KHTKR_OP_UNARY_PLUS:
		case KHTKR_OP_UNARY_MINUS:
		case KHTKR_OP_UNARY_INC:
		case KHTKR_OP_UNARY_DEC:
			isunary = TRUE;
			break;

		case KHTKR_OP_UNARY_SIG:
		case KHTKR_OP_UNARY_NOT:
			isunary = TRUE;
			isint = TRUE;
			break;

		case KHTKR_OP_MUL:
		case KHTKR_OP_DIV:
		case KHTKR_OP_MOD:
			isfold = TRUE;
			break;

		case KHTKR_OP_ADD:
			isfold = TRUE;
			isstr = TRUE;
			break;

		case KHTKR_OP_SUB:
			isfold = TRUE;
			break;

		case KHTKR_OP_SHL:
		case KHTKR_OP_SHR:
		case KHTKR_OP_BINARY_AND:
		case KHTKR_OP_BINARY_XOR:
		case KHTKR_OP_BINARY_OR:
			isfold = TRUE;
			isint = TRUE;
			break;

		case KHTKR_OP_LT:
		case KHTKR_OP_LE:
		case KHTKR_OP_EQ:
		case KHTKR_OP_NEQ:
			isfold = TRUE;
			iscmp = TRUE;
			break;
	}

	if (isunary)
	{
		// 단항 연산
		l = tk->child[0];

		if (l && l->type == KHTKT_CONSTANT)
		{
			lr =
				(l->role >= KHTKR_CONST_NULL && l->role <= KHTKR_CONST_LONG) ? KHTKR_CONST_INT :
				(l->role == KHTKR_CONST_FLOAT || l->role == KHTKR_CONST_DOUBLE) ? KHTKR_CONST_FLOAT :
				(l->role == KHTKR_CONST_STRING) ? KHTKR_CONST_STRING :
				(l->role == KHTKR_CONST_WIDESTRING) ? KHTKR_CONST_STRING : KHTKR_UNKNOWN;

			if (lr == KHTKR_CONST_INT)
			{
				// 정수
				tk->value.i64 = _kh_fold_long_unary(l->value.i64, tk->role);
				tk->role = l->role;

				if (tk->role == KHTKR_CONST_NULL)
					tk->value.i64 = 0;
				else if (tk->role == KHTKR_CONST_BOOL)
					tk->value.i64 = tk->value.i64 != 0;
			}
			else if (lr == KHTKR_CONST_FLOAT && !isint)
			{
				// 실수이고 정수형 연산이 아님
				tk->value.dbl = _kh_fold_double_unary(l->value.dbl, tk->role);
				tk->role = l->role;
			}
			else
			{
				// 상수에 대한 단항 연산자는 위의 것밖에 없다 -> 오류
				rr = KHTKR_UNKNOWN;
				goto pos_error_exit;
			}

			tk->type = KHTKT_CONSTANT;
			tk->child[0] = NULL;
			return TRUE;
		}
		else
		{
			// 대상이 상수가 아니므로 패스
		}

	}
	else if (isfold)
	{
		// 이항 연산
		l = tk->child[0];
		r = tk->child[1];

		if ((l && l->type == KHTKT_CONSTANT) &&
			(r && r->type == KHTKT_CONSTANT))
		{
			lr =
				(l->role >= KHTKR_CONST_NULL && l->role <= KHTKR_CONST_LONG) ? KHTKR_CONST_INT :
				(l->role == KHTKR_CONST_FLOAT || l->role == KHTKR_CONST_DOUBLE) ? KHTKR_CONST_FLOAT :
				(l->role == KHTKR_CONST_STRING) ? KHTKR_CONST_STRING :
				(l->role == KHTKR_CONST_WIDESTRING) ? KHTKR_CONST_STRING : KHTKR_UNKNOWN;
			rr =
				(r->role >= KHTKR_CONST_NULL && r->role <= KHTKR_CONST_LONG) ? KHTKR_CONST_INT :
				(r->role == KHTKR_CONST_FLOAT || r->role == KHTKR_CONST_DOUBLE) ? KHTKR_CONST_FLOAT :
				(r->role == KHTKR_CONST_STRING) ? KHTKR_CONST_STRING :
				(r->role == KHTKR_CONST_WIDESTRING) ? KHTKR_CONST_STRING : KHTKR_UNKNOWN;

			if (iscmp)
			{
				if (lr == KHTKR_CONST_INT && rr == KHTKR_CONST_INT)
				{
					// int - int
					tk->value.i64 = _kh_fold_long_cmp(l->value.i64, r->value.i64, tk->role);
				}
				else if (lr == KHTKR_CONST_INT && rr == KHTKR_CONST_FLOAT)
				{
					// int - float
					tk->value.i64 = _kh_fold_double_cmp((double)l->value.i64, r->value.dbl, tk->role);
				}
				else if (lr == KHTKR_CONST_FLOAT && rr == KHTKR_CONST_INT)
				{
					// float - int
					tk->value.i64 = _kh_fold_double_cmp(l->value.dbl, (double)r->value.i64, tk->role);
				}
				else if (lr == KHTKR_CONST_FLOAT && rr == KHTKR_CONST_FLOAT)
				{
					// float - float
					tk->value.i64 = _kh_fold_double_cmp(l->value.dbl, r->value.dbl, tk->role);
				}
				else if (lr == KHTKR_CONST_INT && rr == KHTKR_CONST_STRING)
				{
					// int - string
					tk->value.i64 = _kh_fold_long_cmp_string(l->value.i64, r->value.str, tk->role);
				}
				else if (lr == KHTKR_CONST_FLOAT && rr == KHTKR_CONST_STRING)
				{
					// float - string
					tk->value.i64 = _kh_fold_double_cmp_string(l->value.dbl, r->value.str, tk->role);
				}
				else if (lr == KHTKR_CONST_STRING && rr == KHTKR_CONST_INT)
				{
					// string - int
					tk->value.i64 = _kh_fold_string_cmp_long(l->value.str, r->value.i64, tk->role);
				}
				else if (lr == KHTKR_CONST_STRING && rr == KHTKR_CONST_FLOAT)
				{
					// string - float
					tk->value.i64 = _kh_fold_string_cmp_double(l->value.str, r->value.dbl, tk->role);
				}
				else if (lr == KHTKR_CONST_STRING && rr == KHTKR_CONST_STRING)
				{
					// string - string
					tk->value.i64 = _kh_fold_string_cmp_string(l->value.str, r->value.str, tk->role);
				}
				else
				{
					// 연산자 없음
					goto pos_error_exit;
				}

				tk->role = KHTKR_CONST_BOOL;
			}
			else
			{
				if (lr == KHTKR_CONST_INT && rr == KHTKR_CONST_INT)
				{
					// int - int
					tk->value.i64 = _kh_fold_long_binary(l->value.i64, r->value.i64, tk->role);
					tk->role = K_MAX(l->role, r->role);

					if (tk->role == KHTKR_CONST_NULL)
						tk->value.i64 = 0;
					else if (tk->role == KHTKR_CONST_BOOL)
						tk->value.i64 = tk->value.i64 != 0;
				}
				else if (!isint)
				{
					if (lr == KHTKR_CONST_INT && rr == KHTKR_CONST_FLOAT)
					{
						// int - float
						tk->value.dbl = _kh_fold_double_binary((double)l->value.i64, r->value.dbl, tk->role);
						tk->role = (l->role == KHTKR_CONST_LONG) ? KHTKR_CONST_DOUBLE : K_MAX(l->role, r->role);
					}
					else if (lr == KHTKR_CONST_FLOAT && rr == KHTKR_CONST_INT)
					{
						// float - int
						tk->value.dbl = _kh_fold_double_binary(l->value.dbl, (double)r->value.i64, tk->role);
						tk->role = (r->role == KHTKR_CONST_LONG) ? KHTKR_CONST_DOUBLE : K_MAX(l->role, r->role);
					}
					else if (lr == KHTKR_CONST_FLOAT && rr == KHTKR_CONST_FLOAT)
					{
						// float - float
						tk->value.dbl = _kh_fold_double_binary(l->value.dbl, r->value.dbl, tk->role);
						tk->role = K_MAX(l->role, r->role);
					}
					else if (isstr /*&& tk->role==KHTKR_OP_ADD*/)
					{
						if (lr == KHTKR_CONST_INT && rr == KHTKR_CONST_STRING)
						{
							// int - string
							psz = _kh_fold_long_add_string(l->value.i64, r->value.str);
							tk->role = r->role;
						}
						else if (lr == KHTKR_CONST_FLOAT && rr == KHTKR_CONST_STRING)
						{
							// float - string
							psz = _kh_fold_double_add_string(l->value.dbl, r->value.str);
							tk->role = r->role;
						}
						else if (lr == KHTKR_CONST_STRING && rr == KHTKR_CONST_INT)
						{
							// string - int
							psz = _kh_fold_string_add_long(l->value.str, r->value.i64);
							tk->role = l->role;
						}
						else if (lr == KHTKR_CONST_STRING && rr == KHTKR_CONST_FLOAT)
						{
							// string - int
							psz = _kh_fold_string_add_double(l->value.str, r->value.dbl);
							tk->role = l->role;
						}
						else if (lr == KHTKR_CONST_STRING && rr == KHTKR_CONST_STRING)
						{
							// string - string
							psz = _kh_fold_string_add_string(l->value.str, r->value.str);
							tk->role = l->role;
						}
						else
						{
							// 연산자 없음
							goto pos_error_exit;
						}

						tk->value.str = psz;
					}
					else
					{
						// 연산자 없음
						goto pos_error_exit;
					}
				}
				else
				{
					// 연산자 없음
					goto pos_error_exit;
				}
			}

			tk->type = KHTKT_CONSTANT;
			tk->child[0] = tk->child[1] = NULL;

			return TRUE;
		}
		else
		{
			// 대상이 상수가 아니므로 패스
		}
	}

	return FALSE;

pos_error_exit:
	k_snprintf(sz, 260 - 1, "invalid '%s' operator. (left: '%s', right: '%s').",
		kh_tos_token_role(tk->role), kh_tos_token_role(lr), kh_tos_token_role(rr));
	kh_parse_error(KERR_SYNTAX, sz);

	return FALSE;
}

